﻿#region Namespaces

using System;
using System.Collections;

#endregion

namespace Vibstudio.X4NET.Csv
{
    public interface ICsvDictionary
    {
        int Count { get; }
        bool IsReadOnly { get; }
        ICollection Keys { get; }
        ICollection Values { get; }
        object this[int index] { get; set; }
        void Add(string key, object value);
        CsvDictionary AsReadOnly();
        void Clear();
        IDictionaryEnumerator GetEnumerator();
        void Insert(int index, string key, object value);
        void Remove(object key);
        void RemoveAt(int index);
    }

    [Serializable]
    public class CsvDictionary : ICsvDictionary
    {
        private readonly IEqualityComparer _comparer;
        private readonly int _initialCapacity;
        private readonly bool _readOnly;

        private ArrayList _objectsArray;

        public CsvDictionary()
            : this(0)
        {
        }

        public CsvDictionary(IEqualityComparer comparer)
            : this(0, comparer)
        {
        }

        private CsvDictionary(CsvDictionary dictionary)
        {
            if (dictionary == null)
            {
                throw new ArgumentNullException("dictionary");
            }
            _readOnly = true;
            _objectsArray = dictionary._objectsArray;
            _comparer = dictionary._comparer;
            _initialCapacity = dictionary._initialCapacity;
        }

        public CsvDictionary(int capacity, IEqualityComparer comparer = null)
        {
            _initialCapacity = capacity;
            _comparer = comparer;
        }

        public int Count
        {
            get { return ObjectsArray.Count; }
        }

        public bool IsReadOnly
        {
            get { return _readOnly; }
        }

        public object this[int index]
        {
            get
            {
                DictionaryEntry entry = (DictionaryEntry)ObjectsArray[index];
                return entry.Value;
            }
            set
            {
                if (_readOnly)
                {
                    throw new NotSupportedException("The CsvDictionary is readonly and cannot be modified.");
                }
                if ((index < 0) || (index >= ObjectsArray.Count))
                {
                    throw new ArgumentOutOfRangeException("index");
                }
                DictionaryEntry entry = (DictionaryEntry)ObjectsArray[index];
                object key = entry.Key;
                ObjectsArray[index] = new DictionaryEntry(key, value);
            }
        }

        public ICollection Keys
        {
            get { return new CsvDictionaryKeyValueCollection(ObjectsArray, true); }
        }

        private ArrayList ObjectsArray
        {
            get { return _objectsArray ?? (_objectsArray = new ArrayList(_initialCapacity)); }
        }

        public ICollection Values
        {
            get { return new CsvDictionaryKeyValueCollection(ObjectsArray, false); }
        }

        public void Add(string key, object value)
        {
            if (_readOnly)
            {
                throw new NotSupportedException("The CsvDictionary is readonly and cannot be modified.");
            }
            ObjectsArray.Add(new DictionaryEntry(key, value));
        }

        public CsvDictionary AsReadOnly()
        {
            return new CsvDictionary(this);
        }

        public void Clear()
        {
            if (_readOnly)
            {
                throw new NotSupportedException("The CsvDictionary is readonly and cannot be modified.");
            }
            ObjectsArray.Clear();
        }

        public virtual IDictionaryEnumerator GetEnumerator()
        {
            return new CsvDictionaryEnumerator(ObjectsArray, 3);
        }

        private int IndexOfKey(object key)
        {
            for (int i = 0; i < ObjectsArray.Count; i++)
            {
                DictionaryEntry entry = (DictionaryEntry)ObjectsArray[i];
                object x = entry.Key;
                if (_comparer != null)
                {
                    if (_comparer.Equals(x, key))
                    {
                        return i;
                    }
                }
                else if (x.Equals(key))
                {
                    return i;
                }
            }
            return -1;
        }

        public void Insert(int index, string key, object value)
        {
            if (_readOnly)
            {
                throw new NotSupportedException("The CsvDictionary is readonly and cannot be modified.");
            }
            if ((index > Count) || (index < 0))
            {
                throw new ArgumentOutOfRangeException("index");
            }
            ObjectsArray.Insert(index, new DictionaryEntry(key, value));
        }

        public void Remove(object key)
        {
            if (_readOnly)
            {
                throw new NotSupportedException("The CsvDictionary is readonly and cannot be modified.");
            }
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }
            int index = IndexOfKey(key);
            if (index >= 0)
            {
                ObjectsArray.RemoveAt(index);
            }
        }

        public void RemoveAt(int index)
        {
            if (_readOnly)
            {
                throw new NotSupportedException("The CsvDictionary is readonly and cannot be modified.");
            }
            if ((index >= Count) || (index < 0))
            {
                throw new ArgumentOutOfRangeException("index");
            }
            ObjectsArray.RemoveAt(index);
        }

        private class CsvDictionaryEnumerator : IDictionaryEnumerator
        {
            private readonly int _objectReturnType;

            private readonly ArrayList _objects;

            private int _index = -1;

            internal CsvDictionaryEnumerator(ArrayList array, int objectReturnType)
            {
                _objects = array;
                _objectReturnType = objectReturnType;
            }

            public bool MoveNext()
            {
                _index++;
                return _index < _objects.Count;
            }

            public void Reset()
            {
                _index = -1;
            }

            public object Current
            {
                get
                {
                    if (_objectReturnType == 1)
                    {
                        DictionaryEntry entry = (DictionaryEntry)_objects[_index];
                        return entry.Key;
                    }
                    if (_objectReturnType == 2)
                    {
                        DictionaryEntry entry2 = (DictionaryEntry)_objects[_index];
                        return entry2.Value;
                    }
                    return Entry;
                }
            }

            public DictionaryEntry Entry
            {
                get
                {
                    if ((_index < 0) || (_index >= _objects.Count))
                    {
                        throw new InvalidOperationException();
                    }
                    DictionaryEntry entry = (DictionaryEntry)_objects[_index];
                    DictionaryEntry entry2 = (DictionaryEntry)_objects[_index];
                    return new DictionaryEntry(entry.Key, entry2.Value);
                }
            }

            public object Key
            {
                get
                {
                    if ((_index < 0) || (_index >= _objects.Count))
                    {
                        throw new InvalidOperationException();
                    }
                    DictionaryEntry entry = (DictionaryEntry)_objects[_index];
                    return entry.Key;
                }
            }

            public object Value
            {
                get
                {
                    if ((_index < 0) || (_index >= _objects.Count))
                    {
                        throw new InvalidOperationException();
                    }
                    DictionaryEntry entry = (DictionaryEntry)_objects[_index];
                    return entry.Value;
                }
            }
        }

        private class CsvDictionaryKeyValueCollection : ICollection
        {
            private readonly bool _isKeys;

            private readonly ArrayList _objects;

            public CsvDictionaryKeyValueCollection(ArrayList array, bool isKeys)
            {
                _objects = array;
                _isKeys = isKeys;
            }

            void ICollection.CopyTo(Array array, int index)
            {
                if (array == null)
                {
                    throw new ArgumentNullException("array");
                }
                if (index < 0)
                {
                    throw new ArgumentOutOfRangeException("index");
                }
                foreach (object obj2 in _objects)
                {
                    array.SetValue(_isKeys ? ((DictionaryEntry)obj2).Key : ((DictionaryEntry)obj2).Value, index);
                    index++;
                }
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return new CsvDictionaryEnumerator(_objects, _isKeys ? 1 : 2);
            }

            int ICollection.Count
            {
                get { return _objects.Count; }
            }

            bool ICollection.IsSynchronized
            {
                get { return false; }
            }

            object ICollection.SyncRoot
            {
                get { return _objects.SyncRoot; }
            }
        }
    }
}